package xyz.zyt123.hikvideo.util;

import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import xyz.zyt123.hikvideo.bean.Device;
import xyz.zyt123.hikvideo.config.HikSdkConfig;
import xyz.zyt123.hikvideo.demo.HCNetSDK;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author zhuyuntao
 */
@Data
@Slf4j
public class CaptureMgr {
  private int count = 0;
  private IntByReference mlPort = new IntByReference(-1);
  private int lPlay = -1;
  private String ip;
  private String port;
  private String user;
  private String psw;
  private String path;
  private Device device;

  public CaptureMgr(String ip, String port, String user, String psw, String path, Device device) {
    this.ip = ip;
    this.port = port;
    this.user = user;
    this.psw = psw;
    this.path = path;
    this.device = device;
  }

  public int loginV40() {
    //注册
    HCNetSDK.NET_DVR_USER_LOGIN_INFO strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息
    HCNetSDK.NET_DVR_DEVICEINFO_V40 strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息

    strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
    System.arraycopy(ip.getBytes(), 0, strLoginInfo.sDeviceAddress, 0, ip.length());

    strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
    System.arraycopy(user.getBytes(), 0, strLoginInfo.sUserName, 0, user.length());

    strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
    System.arraycopy(psw.getBytes(), 0, strLoginInfo.sPassword, 0, psw.length());

    strLoginInfo.wPort = Short.parseShort(port);
    strLoginInfo.bUseAsynLogin = false; //是否异步登录：0- 否，1- 是
    strLoginInfo.byLoginMode = 0;  //0- SDK私有协议，1- ISAPI协议
    strLoginInfo.write();
    log.info("loginInfo:{}", strLoginInfo);
    int lUserID = HikSdkConfig.hCNetSDK.NET_DVR_Login_V40(strLoginInfo, strDeviceInfo);
    if (lUserID == -1) {
      log.info("登录失败，错误码为{}", HikSdkConfig.hCNetSDK.NET_DVR_GetLastError());
    } else {
      log.info("{}:设备登录成功！", ip);
    }
    return lUserID;
  }

  public void logout(int lUserID) {
    //退出程序时调用，每一台设备分别注销
    if (HikSdkConfig.hCNetSDK.NET_DVR_Logout(lUserID)) {
      log.info("注销成功");
    }
  }

  public File capture() {
    // 取流解码过程中播放库从解码码流中抓图
    int user = loginV40();
    if (user == -1) {
      return null;
    }
    // 取流
    realPlay(user, 1);
    // 截图
    File file = getPicByPlayCtrl();
    // 退出
    logout(user);
    return file;
  }


  // 取流
  public void realPlay(int userID, int iChannelNo) {
    if (userID == -1) {
      log.info("请先注册");
      return;
    }
    HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
    strClientInfo.read();
    strClientInfo.hPlayWnd = 0;  //窗口句柄，从回调取流不显示一般设置为空
    strClientInfo.lChannel = iChannelNo;  //通道号
    strClientInfo.dwStreamType = 0; //0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
    strClientInfo.dwLinkMode = 0; //连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4- RTP/RTSP，5- RTP/HTTP，6- HRUDP（可靠传输） ，7- RTSP/HTTPS，8- NPQ
    strClientInfo.bBlocked = 1;
    strClientInfo.byProtoType = 0;
    strClientInfo.write();
    //开启预览
    lPlay = HikSdkConfig.hCNetSDK.NET_DVR_RealPlay_V40(userID, strClientInfo, new FRealDataCallBack(), null);
    if (lPlay == -1) {
      int iErr = HikSdkConfig.hCNetSDK.NET_DVR_GetLastError();
      log.info("取流失败" + iErr);
      return;
    }
    log.info("取流成功");
  }

  // 播放库抓图
  public File getPicByPlayCtrl() {
    //取流成功后，延时一段时间保证播放库解码开始
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    IntByReference pWidth = new IntByReference(0);
    IntByReference pHieght = new IntByReference(0);
    boolean bFlag = HikSdkConfig.playControl.PlayM4_GetPictureSize(mlPort.getValue(), pWidth, pHieght);
    if (!bFlag) {
      System.out.println("获取失败：" + HikSdkConfig.playControl.PlayM4_GetLastError(mlPort.getValue()));
    }
    System.out.println(pWidth.getValue());
    System.out.println(pHieght.getValue());
    IntByReference realPicSize = new IntByReference(0);
    int picsize = pWidth.getValue() * pHieght.getValue() * 5;
    xyz.zyt123.hikvideo.demo.HCNetSDK.BYTE_ARRAY picByte = new HCNetSDK.BYTE_ARRAY(picsize);
    picByte.write();
    Pointer pByte = picByte.getPointer();
    boolean b_GetPic = HikSdkConfig.playControl.PlayM4_GetJPEG(mlPort.getValue(), pByte, picsize, realPicSize);
    if (!b_GetPic) {
      log.info("抓图失败：" + HikSdkConfig.playControl.PlayM4_GetLastError(mlPort.getValue()));
    }
    picByte.read();
    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
    String newName = sf.format(new Date());
    FileOutputStream fout = null;
    try {
      boolean mkdir = new File(path).mkdirs();
      log.info("mkdir:{}", mkdir);
      fout = new FileOutputStream(path + newName + ".jpg");
      //将字节写入文件
      long offset = 0;
      ByteBuffer buffers = pByte.getByteBuffer(offset, realPicSize.getValue());
      byte[] bytes = new byte[realPicSize.getValue()];
      buffers.rewind();
      buffers.get(bytes);
      fout.write(bytes);
      fout.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return new File(path + newName + ".jpg");
  }


  public class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {
    //预览回调
    public void invoke(int lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, Pointer pUser) {
      if (count == 100) {//降低打印频率
        log.info("码流数据回调...dwBufSize=" + dwBufSize);
        count = 0;
      }
      count++;
      //播放库解码
      switch (dwDataType) {
        case HCNetSDK.NET_DVR_SYSHEAD: //系统头
          if (!HikSdkConfig.playControl.PlayM4_GetPort(mlPort)) //获取播放库未使用的通道号
          {
            break;
          }
          if (dwBufSize > 0) {
            if (!HikSdkConfig.playControl.PlayM4_SetStreamOpenMode(mlPort.getValue(), PlayCtrl.STREAME_REALTIME))  //设置实时流播放模式
            {
              break;
            }
            if (!HikSdkConfig.playControl.PlayM4_OpenStream(mlPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) //打开流接口
            {
              break;
            }
            if (!HikSdkConfig.playControl.PlayM4_Play(mlPort.getValue(), null)) //播放开始
            {
              break;
            }

          }
        case HCNetSDK.NET_DVR_STREAMDATA:   //码流数据
          if ((dwBufSize > 0) && (mlPort.getValue() != -1)) {
            if (!HikSdkConfig.playControl.PlayM4_InputData(mlPort.getValue(), pBuffer, dwBufSize))  //输入流数据
            {
              break;
            }
          }
      }
    }
  }
}
